// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.parser;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.xpath.XPathConstants;

import net.web2me.api.ParserException;
import net.web2me.util.DomUtil;
import net.web2me.util.Downloader;
import net.web2me.util.SiteStreamGenerator;
import net.web2me.util.UtilityException;
import net.web2me.util.XPathResolver;

import static net.web2me.api.CommonConstants.*;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class ImageFapParser extends AbstractBaseParser {

	private static final String WELCOME_1 = "welcome=1";

	private static final String HTTP_WWW_IMAGEFAP_COM = "http://www.imagefap.com/";

	private static final String HTTP = "http://";

	private static final String PAGE = "&page=";

	private static final String FAP_TO = "fap.to";

	private static final String PAGE_0 = "&page=0";

	private static final String IMAGE_JPEG = "image/jpeg";

	private Logger logger = Logger.getLogger(this.getClass());

	private String imageStoreDir = EMPTY;

	List<String> otherPages = new ArrayList<String>();

	List<String> processedPages = new ArrayList<String>();

	private boolean firstCall = true;

	private String galleryNamePath = "//font[@face]";

	private Document document;
	
	List<Thread> threads = new ArrayList<Thread>();

	public String getImageStoreDir() {
		return imageStoreDir;
	}

	public void setImageStoreDir(String imageStoreDir) {
		this.imageStoreDir = imageStoreDir;
	}
	
	private void addThread(Thread t) {
		synchronized (threads) {
			threads.add(t);
		}
	}
	
	private boolean checkThreads() {
		synchronized (threads) {
			for(Iterator<Thread> it = threads.iterator(); it.hasNext();) {
				Thread t = it.next();
				
				if(!t.isAlive()) {
					it.remove();
				}
			}
			
			return threads.size() == 0;
		}
	}

	public void process(String siteStream) throws ParserException {
		logger.info("Parsing ImageFap");

		try {
			document = DomUtil.generateDocument(siteStream);
		} catch (Exception e) {
			throw new ParserException(e);
		}

		logger.debug("Loading the page links");
		NodeList links = document.getElementsByTagName(ANCHOR);
		logger.debug(links.getLength() + " links found");

		this.setImageStoreDir(buildDestinyDir(getDirName()));

		List<String> images = new ArrayList<String>();

		logger.info("Scanning document for images");

		for (int i = 0; i < links.getLength(); i++) {

			Element link = (Element) links.item(i);
			String href = EMPTY;

			// Here I check that the link corresponds to a image link and also
			// that
			// is not an absolute link.
			if (((href = link.getAttribute(HREF)).indexOf("image.php?id=") > -1)
					&& !((link.getAttribute(HREF)).indexOf(HTTP) > -1)) {
				images.add(parseImageLink(href));
			} else if ((href = HTTP_WWW_IMAGEFAP_COM
					+ link.getAttribute(HREF)).indexOf(PAGE) > -1) {
				if (firstCall || !otherPages.contains(href)) {
					if ((!otherPages.contains(href))
							&& !(href.indexOf(PAGE_0) > -1)
							&& !(href.indexOf(FAP_TO) > -1)) {
						logger.debug("Adding page: " + href);
						otherPages.add(href);
					}
				}
			}
		}

		logger.debug("Starting threaded downloading");
		Thread imageThread;

		for (int i = 0; i < images.size(); i++) {
			Downloader downloader = new Downloader();
			downloader.setDestinyDirectory(imageStoreDir);
			downloader.setUrl(images.get(i));
			downloader.setExpectedContentType(IMAGE_JPEG);

			logger.debug("Adding a new thread");

			imageThread = new Thread(downloader, images.get(i));
			addThread(imageThread);
			imageThread.start();
		}

		if (firstCall) {
			firstCall = false;

			for (String page : otherPages) {
				String newPage = EMPTY;
				try {
					newPage = SiteStreamGenerator.getStream(page);
				} catch (UtilityException e) {
					throw new ParserException(e);
				}

				this.process(newPage);
			}
		}
		
		while(!checkThreads()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				logger.error(e);
			}
		}
	}

	/**
	 * Stores the given image in the gallery directory
	 * 
	 * @param imageUrl
	 *            The URL of the image
	 * @throws ParserException
	 */
	private String parseImageLink(String imageUrl) throws ParserException {
		String url = EMPTY;

		if (imageUrl.indexOf(HTTP) > -1 && imageUrl.endsWith(DOT_JPG)) {
			return imageUrl;
		}

		url = HTTP_WWW_IMAGEFAP_COM + imageUrl;

		logger.info("Trying to save: " + url);

		String imagePage = EMPTY;
		try {
			imagePage = SiteStreamGenerator.getStream(url, WELCOME_1);
		} catch (UtilityException e) {
			logger.error(e);
		}

		int escapedBegins = imagePage.indexOf("return lD('")
				+ "return lD('".length();
		int escapeEnds = imagePage.indexOf("'", escapedBegins);

		String escapedImageLink = imagePage
				.substring(escapedBegins, escapeEnds);
		byte[] asciiValues = null;
		try {
			asciiValues = escapedImageLink.getBytes("ASCII");
		} catch (UnsupportedEncodingException e1) {
			logger.error("UnsupportedEncodingException caught", e1);
		}

		int lastNumber = Integer.valueOf(escapedImageLink
				.substring(escapedImageLink.length() - 1));

		StringBuilder finalLink = new StringBuilder();

		for (int i = 0; i < asciiValues.length - 1; i++) {
			int tmpNumber = asciiValues[i] - lastNumber;
			char aChar = new Character((char) tmpNumber);

			finalLink.append(aChar);
		}

		/*
		 * kwws=22lpdjhv1lpdjhids1frp2lpdjhv2ixoo26;276827686486;1msj3
		 * 
		 * function lD(s){ var s1=unescape(s.substr(0,s.length-1)); var t='';
		 * 
		 * for(i=0;i<s1.length;i++)
		 * t+=String.fromCharCode(s1.charCodeAt(i)-s.substr(s.length-1,1));
		 * alert(t); return unescape(t); }
		 */

		String link = finalLink.toString();

		logger.debug("Final image url: " + link);

		return link;
	}

	private String getDirName() throws ParserException {
		logger.debug("Calculating the gallery directory name");

		XPathResolver resolver = new XPathResolver(galleryNamePath, document);

		String galleryName = null;
		try {
			galleryName = (String) resolver.resolve(XPathConstants.STRING);

			// Removing dangerous characters
			galleryName = galleryName.replace("/", "_");
			galleryName = galleryName.replace("?", "_");
			galleryName = galleryName.replace(":", "_");
			galleryName = galleryName.replace("#", "_");
		} catch (UtilityException e1) {
			logger.warn("UtilityException caught", e1);
		}

		String galleryId = getUrl().toString().replaceAll("\\D", EMPTY);

		logger.debug("Gallery ID after regex replacing: " + galleryId);

		if (galleryName == null || EMPTY.equals(galleryName)) {
			return galleryId;
		}

		return galleryName + " (" + galleryId + ")";
	}

	private String buildDestinyDir(String dir) {
		String tempDir = this.getDir() + "/" + dir;
		File finalDir = new File(tempDir);

		if (!finalDir.exists()) {
			if (!finalDir.mkdirs()) {
				logger.error("The directory " + tempDir
						+ " could not be created");
			} else {
				logger.info("The directory " + tempDir + " has been created");
			}
		} else {
			logger.info("The directory " + tempDir + " already exists");
		}

		return tempDir;
	}

	public String getDescription() {
		return "Parses the galleries from ImageFap (http://www.imagefap.com)";
	}

	public String getName() {
		return "ImageFap";
	}

}
